using Quartz;
using Quartz.Impl;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Collections.Concurrent;
using Model.Core.Entity;
using Model.Core.Entity.Dbcontexts;
using Model.Core.Services;
using Microsoft.EntityFrameworkCore;
using RobotClient.Controllers;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace RobotClient.Services
{
    public class JobScheduler : IDisposable
    {
        private readonly IScheduler _scheduler;
        private readonly IDbContextFactory<DbcontextBase> _scopeFactory;
        //private readonly DbcontextBase _dbContext;
        private readonly ILogger<JobScheduler> _logger;
        private readonly LocalRobotInfo localRobotInfo;
        private readonly SemaphoreSlim _lock = new(1, 1);
        private ConcurrentDictionary<string, string> _currentTriggers = new();

        public JobScheduler(IDbContextFactory<DbcontextBase> contextFactory, ISchedulerFactory schedulerFactory, ILogger<JobScheduler> logger, LocalRobotInfo localRobotInfo)
        {
            _scopeFactory = contextFactory;
            _logger = logger;
            this.localRobotInfo = localRobotInfo;
            _scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
        }

        public async Task RefreshTriggers()
        {
            await _lock.WaitAsync();
            try
            {
                var newTriggers = await GetLatestCronConfigs();
                var changes = DetectTriggerChanges(newTriggers);

                await ApplyTriggerChanges(changes);
                LogConfigurationChanges(changes);
            }
            finally
            {
                _lock.Release();
            }
        }

        private async Task<Dictionary<string, string>> GetLatestCronConfigs()
        {
            // 问题在于当前上下文中不存在 _scopeFactory，需要确保在类中正确定义该字段。
            // 从构造函数中可以看到，已经将 IServiceScopeFactory 作为参数传入，因此可以直接使用该字段。
            // 同时，确保在使用之前已经正确初始化。

            return await Task.Run(async () =>
            {
                var dbContext = _scopeFactory.CreateDbContext();
                var data = await dbContext.JobCroninfo
                    .Include(j => j.JobId)
                    .Where(j => j.RobotId == localRobotInfo.RobotId)
                    .ToListAsync();
                Dictionary<string, string> keyValuedic = new Dictionary<string, string>();

                foreach (var item in data)
                {

                    if (item.JobId?.Id == null)
                    {
                        _logger.LogError("发现无效的JobCroninfo记录 ID:{ItemId} 缺少Job关联 [时间配置:{CronTime} 机器人ID:{RobotId}]",
                            item.Id, item.Time, localRobotInfo.RobotId);
                        continue;
                    }
                    keyValuedic.Add(item.Time, item.JobId.Id.ToString());
                }
                return keyValuedic;
            });



        }

        private TriggerChanges DetectTriggerChanges(Dictionary<string, string> newTriggers)
        {
            var changes = new TriggerChanges();

            foreach (var binary in newTriggers)
            {
                var cron = binary.Key;
                var jobid = binary.Value;
                if (!_currentTriggers.TryGetValue(cron, out var existingJobId))
                {
                    changes.AddedTriggers.Add(cron);
                    changes.NewTriggers[cron] = jobid;
                }
                else if (existingJobId != jobid)
                {
                    changes.UpdatedTriggers.Add(cron);
                    changes.NewTriggers[cron] = jobid;
                }
            }
            changes.RemovedTriggers.AddRange(_currentTriggers.Keys
                .Except(newTriggers.Keys)
                .ToList());

            return changes;
        }

        private async Task ApplyTriggerChanges(TriggerChanges changes)
        {
            var triggerKeyComparer = new TriggerKeyComparer();

            foreach (var cron in changes.RemovedTriggers)
            {
                var triggerKey = new TriggerKey($"{cron}_trigger", "robot_triggers");
                await _scheduler.UnscheduleJob(triggerKey);
                _currentTriggers.TryRemove(cron, out _);
            }

            foreach (var cron in changes.AddedTriggers.Concat(changes.UpdatedTriggers))
            {

                var jobid = changes.NewTriggers[cron];
                try
                {
                    var jobDetail = JobBuilder.Create<DoJob>()
                    .WithIdentity($"{cron}_job", "robot_jobs")
                    .UsingJobData("jobId", jobid)
                    .Build();
                    var data = ConvertTimeTohm(cron);
                    var trigger = TriggerBuilder.Create()
                        .WithIdentity($"{cron}_trigger", "robot_triggers")
                        .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(data.h, data.m))
                        .ForJob(jobDetail)
                        .Build();


                    await _scheduler.ScheduleJob(jobDetail, trigger);
                    _logger.LogInformation("成功注册定时任务: {Cron} => JobID {JobId}", cron, jobid);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "定时任务注册失败: {Cron} => JobID {JobId}", cron, jobid);
                    continue;
                }
                _currentTriggers[cron] = jobid;
            }
        }

        private void LogConfigurationChanges(TriggerChanges changes)
        {
            if (changes.HasChanges)
            {
                _logger.LogInformation("调度策略更新：新增{added}个，更新{updated}个，移除{removed}个",
                    changes.AddedTriggers.Count,
                    changes.UpdatedTriggers.Count,
                    changes.RemovedTriggers.Count);
            }
        }

        public void Dispose()
        {
            _lock.Dispose();
            GC.SuppressFinalize(this);
        }

        private class TriggerChanges
        {
            public Dictionary<string, string> NewTriggers { get; } = new();
            public List<string> AddedTriggers { get; } = new();
            public List<string> UpdatedTriggers { get; } = new();
            public List<string> RemovedTriggers { get; } = new();
            public bool HasChanges => AddedTriggers.Count + UpdatedTriggers.Count + RemovedTriggers.Count > 0;
        }

        private static string ConvertTimeToCron(string time)
        {
            string[] timeParts = time.Split(':');
            return $"{timeParts[0]} {timeParts[1]} * * ?";
        }
        private static (int h, int m) ConvertTimeTohm(string time)
        {
            string[] timeParts = time.Split(':');
            var h = int.Parse(timeParts[0]);
            var m = int.Parse(timeParts[1]);
            return (h, m);
        }

        private class TriggerKeyComparer : IEqualityComparer<TriggerKey>
        {
            public bool Equals(TriggerKey x, TriggerKey y) => x?.Name == y?.Name;
            public int GetHashCode(TriggerKey obj) => obj.Name.GetHashCode();
        }
    }
}